---
slug: welcome
title: react基础
authors: []
tags: [facebook, hello, docusaurus]
---


react: React框架的核心

react-dom：React视图渲染的核心，基于react构建webapp



```shell
## 全局安装react脚手架
npm i create-react-app -g

## 查看react脚手架版本
create-react-app --version

# 创建react工程
create-react-app 项目名


# 使得react可以配置
npm run eject
```


##### 环境变量

```shell
npm install cross-env
```

##### package.json修改

```json
"scripts": {

"start": "cross-env PORT=8080 node scripts/start.js",

 "build": "node scripts/build.js",

"test": "node scripts/test.js"

},
```

`cross-env PORT=8080` 将环境变量的PORT修改为8080


#### 处理跨域

新建 `src/setupProxy.js`

安装 `http-proxy-middleware` (实现跨域代理的模块，webpack-dev-server的跨域代理原理也是基于他完成的)

```shell
npm install http-proxy-middleware
```


在setupProxy.js中

```javascript
//react proxy跨域代理

  

// npm install http-proxy-middleware

  

const {createProxyMiddleware} = require('http-proxy-middleware');

  

module.exports = function(app){

    app.use(

        //对于/jian开头的请求执行代理

        createProxyMiddleware("/jian",{

            target:"https://www.jianshu.com/asimov",

            changeOrigin:true,

            ws:true,

            pathRewrite:{"^/jian":""}//路径重写

        })

    );

  

    app.use(

        createProxyMiddleware("/api",{

            target:"http://localhost:9090",

            changeOrigin:true,

            ws:true,

            pathRewrite:{"^api":""}

        })

    )

}
```


**注意：**

必须保证地址可用，代理中间件会在开发服务器启动时立即尝试连接目标地址，如果目标地址无效，则react服务器崩溃


### {} 语法

> return 和其他语句之间**不能换行**

number/string 是什么就渲染什么

boolean/null/undefined/Symbol/BigInt: 渲染内容为空

普通对象: 无法渲染会报错

数组: 将数组的每一个元素渲染

函数对象： 不支持在{} 中渲染，但是可以作为函数组件



#### 练习 ： 根据flag的true/false判断元素是否显示

##### 第一种方式

```jsx
let flag = false;

root.render(
<>
<button style={{display:flag ? 'block' : 'none'}}>btn</button>
</>
)
```

##### 第二张方式

```jsx
let flag = false;
root.render(
<>
{flag ? <button>btn</button> : null}
</>

)
```


#### 循环

```jsx
let data = [

  {

    id: 1,

    title: 'first'

  },

  {

    id: 2,

    title: 'second'

  }

]

  

root.render(

  <>

    <h2 className='title'>today news</h2>

    <ul className='news-box'>

      {data.map((item, index) => {

        return <li key={index}>

          <em>

            {index+1}

          </em>

          <span>{item.title}</span>

        </li>

      })}

    </ul>

  </>

)
```

---

## 样式

### 行内样式

> 需要基于对象的格式处理，直接写样式字符串会报错

```jsx
<h2 style={{
color:'red',
fontSize:'18px' //要基于驼峰命名法处理
}}>hello world</h2>
```


### 样式类名

>需要把class替换为className,然后引入外部的css/less/scss文件

```jsx
import './index.css';
<h2 className='box'>hello world</h2>
```

---

### 驼峰命名法：

**小驼峰 camelCase** ： `<inputComp/>`

**大驼峰  PascalCase** :  `<InputComp/>`

还有一种 **kabab-case**  : `<input-comp/>`


## React获取真实dom


| 组件类别 | 受控组件                    | 非受控组件                           |
| ---- | ----------------------- | ------------------------------- |
| 区别   | 基于修改数据/状态，让视图更新，达到需要的效果 | 基于ref获取DOM元素，我们操作DOM元素，来实现需求和效果 |
| 推荐程度 | 推荐                      | 偶尔                              |

#### 对于类组件使用ref获取dom元素 ,使用this.ref.current获取dom

```jsx
import React from "react";

  

class Demo extends React.Component{

    constructor(props){

        super(props);//必须调用super

        this.divRef = React.createRef();//必需

    }

    render(){

        return <>

        <div ref={this.divRef}>hello world</div>

        </>

    }

    componentDidMount(){

        //第一次渲染完毕，virtualDOM已经转换为真实DOM此时可以获取DOM元素

        console.log(this.divRef.current);

    }

}

  

export default Demo;
```


## react性能优化memo

> memo会对给定的组件做浅比较，如果组件没变化则不会重新渲染

**不止函数式组件，类组件也可以使用memo**


```jsx
const Children = React.memo(
()=>{

	return (
	<>
	div
	</>
	)

}

)
```

## setState

> 更新状态

```jsx
this.setState({

},()=>{

})
```

`this.setState(particleState,callBack)`


| 参数            | 作用                                                                        |
| ------------- | ------------------------------------------------------------------------- |
| particelState | {},指定的状态更改，`this.setState({x:100})` 我们只修改了x，其余状态不动                        |
| callBack      | 状态更改，视图更新完毕后触发执行，发生在componentDidUpdate之后，componentDidUpdate会在任何状态更改后都触发执行 |
> 在React18中，setState在任何地方执行，都是“异步操作”

- 在React18中有一套更新队列的机制
- 基于异步操作，实现状态的“批处理”
好处：
- 减少视图更新的次数，降低渲染消耗的性能
- 让更新的逻辑和流程更清晰&稳健

#### updater

> setState的更新队列,目的是让setState执行一次，但是setTimeOut里面的setState不会被合在一起执行


```jsx
import React from 'react';  
  
/**  
 * 研究一下setState在碰到setTimeOut时的行为  
 */  
class SetStateSetTimeOut extends React.Component {  
  
    state = {  
        x:0,  
        y:0,  
        z:0  
    }  
  
    onClickHandler = () => {  
        this.setState({  
            x:this.state.x+1  
        })  
  
        this.setState({  
            y:this.state.y+1  
        })  
        //上面两个setState会被合并为一次更新  
        //setTimeout本身就是异步的，所以会单独占用一次更新  
  
        setTimeout(()=>{  
            this.setState({  
                z:this.state.z+1  
            })  
        },100)  
    }  
  
  
    render() {  
        console.log("render")  
        return (  
            <>  
                <div>
                    x:{this.state.x}  
                    y:{this.state.y}  
                    z:{this.state.z}  
                </div>  
                <button onClick={this.onClickHandler}>  
                    click handler  
                </button>  
            </>        
        )  
    } 
}  
  
export default SetStateSetTimeOut;
```


#### flushSync（不推荐，牺牲性能）

`flushSync` 可以让updater更新队列立刻刷新一次

例：
```jsx
import React from "react";  
import {flushSync} from "react-dom";  
  
class StateDemo extends React.Component {  
    state = {  
        x:1,  
        y:1,  
        z:1  
    }  
  
    handlerClick = () => {  
        //箭头函数this指向实例【宿主环境】  
        let {x,y,z} = this.state;  
  
  
        flushSync(()=>{  
            this.setState({  
                x:this.state.x+1  
            })  
  
            this.setState({  
                y:this.state.y+1  
            })  
        });  
        //setState是异步的，如果不加flushSync则取不到最新的state  
        this.setState({  
            z:this.state.x+this.state.y  
        })  
    }  
  
    render() {  
        return (  
  
            <div>  
                <div>
                x:{this.state.x} y:{this.state.y} z:{this.state.z}  
                </div>  
                <button onClick={this.handlerClick}></button>  
            </div>        )  
    }  
}  
  
export default StateDemo;
```

#### prevState

```jsx
this.setState(  
    prevState=>({  
        x:prevState.x+1  
    })  
)
//或者换个写法

this.setState(prevState=>{  
    return {  
        x:prevState.x+1  
    }  
})
```
#### setState在碰到for循环的场景

> 下面的场景中为什么最后x不是10？react合并了更新

> 第一次是`setState({x:0+1})`;第二次也是`setState({x:0+1})`,一直取到的都是最开始的0

**为什么不更新x呢？**

闭包

__解决问题__

```jsx
//函数式setState解决问题  
this.setState(  
    prevState=>({  
        x:prevState.x+1  
    })  
)
//或者换个写法

this.setState(prevState=>{  
    return {  
        x:prevState.x+1  
    }  
})
```

```jsx
import React from 'react'  
  
export  default class SetStateForTest extends React.Component {  
  
    state = {  
        x:1,  
        y:1,  
        z:1  
    }  
  
    onClickHandler = () => {  
        for(let i = 1; i < 10;i++){  
            this.setState({  
                x:this.state.x+1  
            })  
        }  
    }  
  
    render() {  
        return (  
            <>  
                <div>                    
                    x:{this.state.x}  
                    y:{this.state.y}  
                    z:{this.state.z}  
                </div>  
  
                <button onClick={this.onClickHandler}>  
                    click  
                </button>  
            </>        
        )  
    }  
  
}
```


## 合成事件

> on开头的事件都是合成事件

合成事件绑定的如果是普通函数，则函数内的this是**undefined**

```jsx

handlerClick(){
//这里的this是undefined
}

<button onClick={this.handlerClick}></button>
```

如果是箭头函数则this指向了实例

```jsx
import React from 'react'  
  
/**  
 * 合成事件  
 */  
export  default class MergeEvents extends React.Component {  
  
    state = {  
        x:0,  
        y:0,  
        z:0  
    }  
  
    handler = (ev)=>{  
        console.log(this);//实例.handler this指向实例  
        console.log(ev);//合成事件对象  
    }  
  
    handlerWithArg = (x,y,ev)=>{  
        //最后一个一定是合成事件对象  
        console.log(x,y,ev);  
    }  
  
    render() {  
        return (  
            <>  
                <div>                    
	                x:{this.state.x};  
                    y:{this.state.y},  
                    z:{this.state.z}  
                </div>  
                <button onClick={this.handler}>one</button>  
                <button onClick={this.handlerWithArg.bind(null,1,2)}>
	                two
                </button>  
            </>        
            
        )  
    }  
  
}
```


```jsx
import React from 'react'  
  
/**  
 * 合成事件  
 */  
export  default class MergeEvents extends React.Component {  
  
    state = {  
        x:0,  
        y:0,  
        z:0  
    }  
  
    handler = (ev)=>{  
        console.log(this);//实例.handler this指向实例  
        console.log(ev);//合成事件对象  
    }  
  
    handlerWithArg = (x,y,ev)=>{  
        //最后一个一定是合成事件对象  
        console.log(x,y,ev);  
    }  
  
    handlerWithNoThis = (ev)=>{  
        console.log(ev);//合成事件对象  
        console.log(this);//获取不到this为undefined  
    }  
  
    handlerWithBindGetThis = (ev)=>{  
        console.log(ev);  
        console.log(this);//获取得到this,bind绑定的this  
    }  
  
    render() {  
        return (  
            <>  
                <div>                    
	                x:{this.state.x};  
                    y:{this.state.y},  
                    z:{this.state.z}  
                </div>  
                <button onClick={this.handlerWithNoThis}>
	                普通函数无法获取this
                </button>  
                {/*为什么不用call或apply?call或apply会直接触发函数*/} 
                <button onClick={this.handlerWithBindGetThis.bind(this)}>
                普通函数使用bind绑定this
                </button>  
                
                 
                <button onClick={this.handler}>
                箭头函数获取合成事件对象与this
                </button>  
                <button onClick={this.handlerWithArg.bind(null,1,2)}>
                使用bind传递多余参数
                </button>  
            </>        
        )  
    }  
  
}
```

#### 原理

> react的合成事件都是基于事件委托处理的


